﻿#ifndef CPLCSCENE_H
#define CPLCSCENE_H

//自定义的场景类，重写了鼠标事件，用于激活元件选中状态

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QList>
#include <QMap>
#include <QTimer>

#include "SPlcGlobalDefine.h"

#include "IPlcBaseItem.h"
#include "CPlcItemContact.h"
#include "CPlcItemSpace.h"
#include "CPlcItemLinkLine.h"
#include "CPlcItemCoil.h"
#include "CPlcItemBranch.h"
#include "CPlcItemJump.h"
#include "CPlcItemComment.h"
#include "CPlcItemFunctionBlock.h"

using namespace plc;

class CPlcScene : public QGraphicsScene
{
    typedef struct
    {
        int32_t x;   //行号
        int32_t y;   //列号
    } SPosition;

    typedef struct
    {
        bool last;
        bool current;
    } SItemValue;

    typedef QList<SItemData> rowList;

    Q_OBJECT
public:
    CPlcScene(QGraphicsScene *parent = NULL);
    ~CPlcScene();

    //初始化元件场景，加载一行空元件
    void initScene();

    //用于外部调整场景宽
    void updateSceneWidth(double width);

    //复位图元的激活选中状态
    void resetItemSelectStatus();

    //获取选中的Item图元
    IPlcBaseItem *getSelectItem();
    //获取选中的Item图元数据
    bool getSelectItemData(SItemData &itemData);

    //向前插入元件 默认插入一个空白元件
    void insertItem();
    //替换元件
    void replaceItem(SItemData &itemData);
    void replaceItem(SItemData &itemData, const SPosition pos);

    //添加分支
    void addBranch();
    //移除分支
    void removeBranch();

    //向前插入空白行
    void insertRowForward();
    void insertRowForward(int32_t row);
    //向后插入空白行
    void insertRowBackward(int32_t row);

    //移除行
    void removeCurrentRow();
    void removeCurrentRow(int32_t row);

    //移除item,实际处理为替换空白的item
    void removeItem();

    //撤销
    void onUndo();
    //还原
    void onRedo();

    //放大
    void onZoomIn();
    //缩小
    void onZoomOut();

    //根据变量名查找Item位置
    void findItem(QString variable);

    //更新注释列表
    void updateCommentList(/*QMap<QString,QString> &comment*/);
    //更新IO值数据
    void updateIoValue(/*QMap<QString,QString> &comment*/);

    //设置注释是否可见
    void setCommentView(bool value);

    //编译梯形图
    void compileLad();

    //切换到监控梯形图
    void switchToMonitorLad();
    //切换到编辑梯形图
    void switchToEditLad();

private:
    //重写鼠标点击松开事件
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    //重写键盘按下事件
    void keyPressEvent(QKeyEvent *event) override;

private:
    //从指定的列表中，查找指定item的位置
    bool
    findItem(const QList<rowList> &list, IPlcBaseItem *item, SPosition &pos);
    //判断两个元件类型 大类是否相同
    bool typeCmp(uint8_t type1, uint8_t type2);
    //创建元件
    IPlcBaseItem *createItem(QRectF rect, uint8_t type, QString variable);
    //重新设置场景宽度，同时动态调整元件大小
    void setSceneWidth(double width);
    //重写的移除函数 添加了析构item内容
    void removeItem(IPlcBaseItem *item);
    //分割列表，把完整列表分割成固定行长度的列表
    void partItemList(QList<rowList> &fullList, QList<rowList> &partList);
    //对齐列表，列表列数不一致时，对齐列表到和最长列一致
    void alignItemList(QList<rowList> &list);

    //根据指定Item行查找关联行范围
    void findRelateItemRow(const QList<rowList> &list,
                           int32_t selRow,
                           int32_t &upRow,
                           int32_t &downRow);

    //向下合并指定起始行的梯形图列表，把所有跳转关系合并  返回遍历的最大行深度
    //移除原列表被合并的内容
    bool mergeRelateItemRow(const QList<rowList> &list,
                            QList<rowList> &fullList,
                            int32_t selRow,
                            int32_t &startRow,
                            int32_t &depth);

    //在指定列插入空元件或者连接线元件，如果列能够后推，则后推一格
    void insertColumnOfList(QList<rowList> &list, int32_t column);

    //输出日志
    void printLog(QString str = "");

    //撤销还原相关功能实现
private:
    //从场景中移除所有元件
    void removeAll(QList<rowList> &list);
    //加载所有元件到场景
    void loadAll(QList<rowList> &list);
    //添加一个记录步骤
    void addStep();

    //梯形图编译相关
private:
    //保存梯形图
    void saveItemList(QList<rowList> &list, QString path = "");
    //读取保存的梯形图
    bool readItemList(QList<rowList> &list, QString path = "");
    //通过变量名类型，生成变量名
    QString generateVariableName(SComponent &component);

    //检测梯形图语法合法性
    bool checkIllegal(QList<rowList> &list);

    //整理梯形图,使之有序
    void inOrderItemList(QList<rowList> &list);

    //梯形图运行监控
private:
    //运行监控梯形图
    void runMonitorLad(QList<rowList> &list);
    //创建梯形图原理值列表
    QList<QList<SItemValue> > crateValueList(QList<rowList> &list);

    //处理单个元件的逻辑  同时获取跳转关系最大层级数
    void operateOneItemLogic(QList<rowList> &list,
                             QList<QList<SItemValue> > &valueList,
                             SPosition pos);
    //获取跳转关系最大层级数:
    int32_t getMaxJumpNum(QList<rowList> &list);

private:
    //键盘选中操作：上下左右
    void onLeftSelectItem();
    void onRightSelectItem();
    void onUpSelectItem();
    void onDownSelectItem();

private:
    bool mInitialStatus;   //初始化状态
    double mLastWidth;     //之前设置的显示宽度
    double mMixHeight;     //最小高度

    QList<rowList> mListSave;   //不限定长度的元件存储列表
    QList<rowList> mListView;   //用于界面显示，限定元件长度的存储列表

    QList<QList<rowList> > mSaveStep;   //用于实现前进后退的保存列表
    int32_t mStepIndex;                 //当前步索引

    CPlcItemBranch *mPowerRail;   //左侧电源母线

    QMap<QString, QString> mCommentMap;   //用于注释的map
    QMap<QString, bool> mIoValueMap;      //用于IO变量值的map

    bool mCommentIsVisible;   //注释可见状态

    int32_t mScale;   //缩放比例0~4

    QTimer loopTimer;

signals:
    void sgnSelectItemChanged();   //选择的元件变化时
};

#endif   // CPLCSCENE_H
